<!DOCTYPE HTML>
<html>
<head>
  <title>Basic WebSocket test</title>
  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>

<body onload="testWebSocket()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472529">Mozilla Bug 472529</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">

const kUrl = "ws://mochi.test:8888/tests/dom/base/test/file_websocket_basic";

var gTestElement;
var ws;

function forcegc() {
  SpecialPowers.forceGC();
  SpecialPowers.gc();
}

function testWebSocket() {
  gTestElement = document.getElementById("test");

  SimpleTest.executeSoon(testWebSocket1);
}

/**
 * Sends message keywords, then receives their values.
 */
function testWebSocket1() {
  gTestElement.textContent = "Running testWebSocket1()";

  var results = ["test",
                 "/tests/dom/base/test/file_websocket_basic",
                 "http://mochi.test:8888",
                 "end"];

  ws = new WebSocket(kUrl, "test");
  is(ws.url, kUrl, "[1] WebSocket.url");
  ws.onopen = function(e) {
    const params = ["protocol", "resource", "origin", "end"];

    gTestElement.textContent += "\nSending :";
    for (var i = 0; i < params.length; ++i) {
      gTestElement.textContent += " " + params[i];
      ws.send(params[i]);
    }

    // Set this before onmessage() is called, so it is displayed once only.
    gTestElement.textContent += "\nReceived:";
  };
  ws.onclose = function(e) {
    is(results.length, 0, "[1] Number of unreceived messages");
    ok(e.wasClean, "[1] Connection closed cleanly");

    SimpleTest.executeSoon(testWebSocket2);
  };
  ws.onerror = function(e) {
    ok(false, "[1] onerror() should not have been called!");
    gTestElement.textContent += "\nonerror() should not have been called!";
    SimpleTest.executeSoon(SimpleTest.finish);
  };
  ws.onmessage = function(e) {
    is(e.data, results[0], "[1] Received message");
    gTestElement.textContent += " " + e.data;
    results.shift();
  };
}

/**
 * Sends 1000+1 test messages, then receives them.
 */
function testWebSocket2() {
  gTestElement.textContent = "Running testWebSocket2()";

  const displayInterval = 100;
  const testCount = 1000;
  const testMessage = "test message 2.";

  var messageCount = 0;

  ws = new WebSocket(kUrl, "test");
  ws.onopen = function(e) {
    gTestElement.textContent += "\nSending :";
    for (var i = 1; i <= testCount; ++i) {
      if (i % displayInterval == 1) {
        gTestElement.textContent += " " + i;
      }
      ws.send(testMessage + i);
    }
    gTestElement.textContent += " end";
    ws.send("end");

    // Set this before onmessage() is called, so it is displayed once only.
    gTestElement.textContent += "\nReceived:";
  };
  ws.onclose = function(e) {
    is(messageCount, testCount + 1, "[2] Number of received messages");
    ok(e.wasClean, "[2] Connection closed cleanly");

    SimpleTest.executeSoon(testWebSocket3);
  };
  ws.onerror = function(e) {
    ok(false, "[2] onerror() should not have been called!");
    gTestElement.textContent += "\nonerror() should not have been called!";
    SimpleTest.executeSoon(SimpleTest.finish);
  };
  ws.onmessage = function(e) {
    ++messageCount;
    if (messageCount > testCount)
      is(e.data, "end", "[2] Received message");
    else
      is(e.data, testMessage + messageCount, "[2] Received message");
    if (messageCount % displayInterval == 1) {
      gTestElement.textContent += " " + messageCount;
    }
  };
}

/**
 * Sends testcount+1 test messages, then receives them, calling forcegc() at each step.
 */
function testWebSocket3() {
  gTestElement.textContent = "Running testWebSocket3() [can take a little while]";

  const displayInterval = 10;
  const testCount = 10;
  const testMessage = "test message 3.";

  var messageCount = 0;

  ws = new WebSocket(kUrl, "test");
  // Set this before onopen() is called,
  // otherwise its display would be delayed by forcegc() calls...
  gTestElement.textContent += "\nSending :";
  ws.onopen = function(e) {
    for (var i = 1; i <= testCount; ++i) {
      forcegc();
      if (i % displayInterval == 1) {
        // Actual display is delayed by forcegc() calls...
        gTestElement.textContent += " " + i;
      }
      ws.send(testMessage + i);
    }
    forcegc();
    gTestElement.textContent += " end";
    ws.send("end");

    // Set this before onmessage() is called, so it is displayed once only.
    gTestElement.textContent += "\nReceived:";
  };
  ws.onclose = function(e) {
    is(messageCount, testCount + 1, "[3] Number of received messages");
    ok(e.wasClean, "[3] Connection closed cleanly");

    SimpleTest.executeSoon(testWebSocket4);
  };
  ws.onerror = function(e) {
    ok(false, "[3] onerror() should not have been called!");
    gTestElement.textContent += "\nonerror() should not have been called!";
    SimpleTest.executeSoon(SimpleTest.finish);
  };
  ws.onmessage = function(e) {
    forcegc();
    ++messageCount;
    if (messageCount > testCount)
      is(e.data, "end", "[3] Received message");
    else
      is(e.data, testMessage + messageCount, "[3] Received message");
    if (messageCount % displayInterval == 1) {
      // Actual display is delayed by forcegc() call(s)...
      gTestElement.textContent += " " + messageCount;
    }
  };
}

/**
 * Sends a huge test message, then receives it, then closes the WebSocket from client-side.
 */
function testWebSocket4() {
  gTestElement.textContent = "Running testWebSocket4()";

  // String length = 13 + ((10,000 - 1) * 26) + 11 = 259,998 = almost 254 KiB.
  const longString = "messageStart " + new Array(10000).join(" -huge WebSocket message- ") + " messageEnd";

  ws = new WebSocket(kUrl, "test");
  ws.onopen = function(e) {
    is(this, ws, "[4, onopen()] 'this' should point to the WebSocket.");
    gTestElement.textContent += "\nSending the huge message";
    ws.send(longString);
  };
  ws.onclose = function(e) {
    is(this, ws, "[4, onclose()] 'this' should point to the WebSocket.");
    ok(e.wasClean, "[4] Connection closed cleanly");

    SimpleTest.executeSoon(testWebSocket5);
  };
  ws.onerror = function(e) {
    is(this, ws, "[4, onerror()] 'this' should point to the WebSocket.");
    ok(false, "[4, onerror()] should not have been called!");
    gTestElement.textContent += "\nonerror() should not have been called!";
    SimpleTest.executeSoon(SimpleTest.finish);
  };
  ws.onmessage = function(e) {
    is(this, ws, "[4, onmessage()] 'this' should point to the WebSocket.");
    // Do not use |is(e.data, longString, "...");| that results in a _very_ long line.
    is(e.data.length, longString.length, "[4] Length of received message");
    ok(e.data == longString, "[4] Content of received message");
    gTestElement.textContent += "\nReceived the huge message";
    this.close();
  };
}

/**
 * Closes the WebSocket from client-side, then sends a test message that should be buffered.
 */
function testWebSocket5() {
  gTestElement.textContent = "Running testWebSocket5()";

  ws = new WebSocket(kUrl, "test");
  ws.onopen = function(e) {
    is(this.bufferedAmount, 0, "[5] Length of empty buffer before closing");
    this.close();
  };
  ws.onclose = function(e) {
    ok(e.wasClean, "[5] Connection closed cleanly");
    is(this.bufferedAmount, 0, "[5] Length of empty buffer after closing");

    var msg = "test message to be buffered";
    this.send(msg);
    is(this.bufferedAmount, msg.length, "[5] Length of buffered message sent after closing");

    gTestElement.textContent += "\ntestWebSocket5() completed";

    SimpleTest.executeSoon(SimpleTest.finish);
  };
  ws.onerror = function(e) {
    ok(false, "[5] onerror() should not have been called!");
    gTestElement.textContent += "\nonerror() should not have been called!";
    SimpleTest.executeSoon(SimpleTest.finish);
  };
}

SimpleTest.waitForExplicitFinish();

</script>
</pre>
</body>
</html>
